package com.maiko.maikoaiagent.agent;

import cn.hutool.core.collection.CollUtil;
import com.alibaba.cloud.ai.dashscope.chat.DashScopeChatOptions;
import com.maiko.maikoaiagent.agent.model.AgentState;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.extern.slf4j.Slf4j;
import org.springframework.ai.chat.messages.AssistantMessage;
import org.springframework.ai.chat.messages.Message;
import org.springframework.ai.chat.messages.ToolResponseMessage;
import org.springframework.ai.chat.messages.UserMessage;
import org.springframework.ai.chat.model.ChatResponse;
import org.springframework.ai.chat.prompt.ChatOptions;
import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.ai.model.tool.ToolCallingManager;
import org.springframework.ai.model.tool.ToolExecutionResult;
import org.springframework.ai.tool.ToolCallback;

import java.util.List;
import java.util.stream.Collectors;

/**
 * ToolCallAgent 是一个实现了工具调用能力的智能代理类。
 * 它继承自 ReActAgent，封装了完整的思考（think）和行动（act）过程。
 *
 * 本类通过 Spring AI 的 ToolCallback 接口集成多个工具，支持大模型根据上下文选择工具并调用。
 * 使用者可以通过继承该类并配置工具，快速实现 Agent 的插件化能力。
 *
 * 工作机制：
 * - think(): 向模型发送当前上下文和工具列表，模型决定是否调用工具
 * - act(): 若模型调用了工具，则执行工具并更新对话历史
 *
 * 支持的模型平台如 DashScope、OpenAI 等（只要符合 Spring AI ChatClient 接口）。
 *
 * @author: Maiko7
 * @create: 2025-07-10 7:52
 */

@EqualsAndHashCode(callSuper = true)
@Data
@Slf4j
public class ToolCallAgent extends ReActAgent {

    /** 可供大模型调用的工具列表（ToolCallbacks 接口） */
    private final ToolCallback[] availableTools;

    /** 上一次模型响应中包含的工具调用信息，用于 act() 阶段执行 */
    private ChatResponse toolCallChatResponse;

    /** 工具调用管理器，负责执行工具调用并构建消息历史 */
    private final ToolCallingManager toolCallingManager;

    /** 控制模型行为的 ChatOptions，这里禁用了自动工具调用机制（需手动控制） */
    private final ChatOptions chatOptions;

    /**
     * 构造函数，初始化工具列表与工具调用管理器
     *
     * @param availableTools 可调用的工具列表
     */
    public ToolCallAgent(ToolCallback[] availableTools) {
        super();
        this.availableTools = availableTools;
        this.toolCallingManager = ToolCallingManager.builder().build();
        // 禁用 Spring AI 默认的自动工具调用，改为手动管理调用和上下文
        this.chatOptions = DashScopeChatOptions.builder()
                .withProxyToolCalls(true)  // 开启代理工具调用模式
                .build();
    }

    /**
     * 智能体进行思考，决定是否需要调用工具
     * 向模型发送上下文和工具列表，并解析模型是否想调用工具
     *
     * @return true 表示模型希望调用工具，false 表示无需行动
     */
    @Override
    public boolean think() {
        // 若设置了 nextStepPrompt，则加入用户提示消息
        if (getNextStepPrompt() != null && !getNextStepPrompt().isEmpty()) {
            UserMessage userMessage = new UserMessage(getNextStepPrompt());
            getMessageList().add(userMessage);
        }

        List<Message> messageList = getMessageList();
        Prompt prompt = new Prompt(messageList, chatOptions);
        try {
            // 向模型发送 Prompt，并携带工具信息
            ChatResponse chatResponse = getChatClient().prompt(prompt)
                    .system(getSystemPrompt())
                    .tools(availableTools)
                    .call()
                    .chatResponse();

            // 保存响应以供后续 act() 使用
            this.toolCallChatResponse = chatResponse;

            // 解析返回的助手消息和工具调用信息
            AssistantMessage assistantMessage = chatResponse.getResult().getOutput();
            String result = assistantMessage.getText();
            List<AssistantMessage.ToolCall> toolCallList = assistantMessage.getToolCalls();

            // 输出日志
            log.info(getName() + " 的思考: " + result);
            log.info(getName() + " 选择了 " + toolCallList.size() + " 个工具");

            // 输出工具调用信息
            String toolCallInfo = toolCallList.stream()
                    .map(toolCall -> String.format("工具名称：%s，参数：%s",
                            toolCall.name(),
                            toolCall.arguments()))
                    .collect(Collectors.joining("\n"));
            log.info(toolCallInfo);

            if (toolCallList.isEmpty()) {
                // 没有工具调用时，记录助手消息（纯对话）
                getMessageList().add(assistantMessage);
                return false;
            } else {
                // 有工具调用，不记录助手消息，由工具调用流程统一管理
                return true;
            }
        } catch (Exception e) {
            // 捕获异常并记录错误消息到对话上下文
            log.error(getName() + " 的思考过程遇到问题: " + e.getMessage());
            getMessageList().add(new AssistantMessage("处理时遇到错误: " + e.getMessage()));
            return false;
        }
    }

    /**
     * 执行模型请求的工具调用，并将调用结果更新到消息历史中
     *
     * @return 工具调用结果的文本描述（用于展示或日志）
     */
    @Override
    public String act() {
        if (!toolCallChatResponse.hasToolCalls()) {
            return "没有工具调用";
        }

        // 构造 Prompt，用于执行工具调用
        Prompt prompt = new Prompt(getMessageList(), chatOptions);
        ToolExecutionResult toolExecutionResult =
                toolCallingManager.executeToolCalls(prompt, toolCallChatResponse);

        // 更新消息历史，包括工具调用的结果
        setMessageList(toolExecutionResult.conversationHistory());

        // 当前工具调用的结果
        ToolResponseMessage toolResponseMessage = (ToolResponseMessage) CollUtil.getLast(toolExecutionResult.conversationHistory());
        String results = toolResponseMessage.getResponses().stream()
                .map(response -> "工具 " + response.name() + " 完成了它的任务！结果: " + response.responseData())
                .collect(Collectors.joining("\n"));
        // 判断是否调用了终止工具
        boolean terminateToolCalled = toolResponseMessage.getResponses().stream()
                .anyMatch(response -> "doTerminate".equals(response.name()));
        if (terminateToolCalled) {
            setState(AgentState.FINISHED);
        }
        log.info(results);
        return results;

    }

}
